package br.uniceub.fsw.arquitetura.persistencia.transacao;

import java.sql.Connection;
import java.sql.SQLException;

import br.uniceub.fsw.arquitetura.persistencia.conexao.ConexaoAtual;
import br.uniceub.fsw.arquitetura.persistencia.exception.BancoDadosException;

/**
 * Classe que representa uma transação da requisição (thread) atual.
 * Transação é uma unidade de trabalho com o banco de dados, sua consistência
 * deve ser mantida, ou seja, caso em algum momento haja uma falha, tudo feito
 * na mesma transação deve ser desfeito (cancelado ou rollback).
 * 
 * @author Daniel Rochetti
 */
public class Transacao {

	/**
	 * Objeto que guarda a transação atual para uma determinada requisição.
	 */
	private static ThreadLocal<Transacao> transacaoCorrente =
			new ThreadLocal<Transacao>();

	private Connection conexao;

	private Transacao(Connection conexao) {
		this.conexao = conexao;
	}

	/**
	 * Inicia uma transação. Esse método deve ser chamado no início de
	 * um método que executa uma lógica de negócio.
	 */
	public static void iniciar() {
		
		ConexaoAtual.setParticipaTransacao(Boolean.TRUE);
		Transacao transacao = transacaoCorrente.get();
		if (transacao == null) {
			Connection conexao = ConexaoAtual.getConexao();

			// Iniciou a transação (no JDBC é iniciada junto com a conexão)
			transacao = new Transacao(conexao);
			transacaoCorrente.set(transacao);
		}
		
	}

	/**
	 * Desfaz (rollback) as operações realizadas nessa transação. Esse metodo
	 * deve ser chamado dentro de um bloco {@code catch} do método de
	 * negócio. 
	 */
	public static void desfazer() {
		
		Transacao transacao = transacaoCorrente.get();
		if (transacao == null) {
			throw new IllegalStateException("Impossível desfazer transação! " +
					"A transação não foi iniciada ou já foi terminada.");
		}
		
		try {
			transacao.conexao.rollback();
			ConexaoAtual.setParticipaTransacao(Boolean.FALSE);
		} catch (SQLException e) {
			throw new BancoDadosException("Erro ao desfazer transação: " +
					e.getMessage(), e);
		} finally {
			transacaoCorrente.set(null);
			ConexaoAtual.liberarConexao();
		}
	}

	/**
	 * Efetiva (commit) todas as operações realizadas no banco. Esse método
	 * deve ser chamado ao final de uma lógica de negócio.
	 */
	public static void efetivar() {
		
		Transacao transacao = transacaoCorrente.get();
		if (transacao == null) {
			throw new IllegalStateException("Impossível efetivar transação! " +
					"A transação não foi iniciada ou já foi terminada.");
		}
		
		try {
			transacao.conexao.commit();
			ConexaoAtual.setParticipaTransacao(Boolean.FALSE);
		} catch (SQLException e) {
			throw new BancoDadosException("Erro ao efetivar transação: " +
					e.getMessage(), e);
		} finally {
			transacaoCorrente.set(null);
			ConexaoAtual.liberarConexao();
		}
	}
	
}
